package com.chixing.listener;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.chixing.config.RabbitMQConfig;
import com.chixing.entity.*;
import com.chixing.entity.DTO.OrderCancelEarlyDTO;
import com.chixing.entity.DTO.RefundsDTO;
import com.chixing.mapper.OrderDetailMapper;
import com.chixing.mapper.OrdersMapper;
import com.chixing.mapper.PenaltyMapper;
import com.chixing.mapper.RefundsMapper;
import com.chixing.service.IOrdersService;
import com.chixing.service.IRoomInventoryService;
import com.chixing.utils.Result;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Map;

/**
 * 监听已支付订单提前取消的队列，处理取消该订单
 */
@Component
public class OrderCancelEarlyListener {

    @Autowired
    private OrdersMapper ordersMapper;
    @Autowired
    private OrderDetailMapper orderDetailMapper;
    @Autowired
    private PenaltyMapper penaltyMapper;
    @Autowired
    private RefundsMapper refundsMapper;
    @Autowired
    private IOrdersService ordersService;
    @Autowired
    private IRoomInventoryService roomInventoryService;

    // 直接注入 RefundsController（可行，因为它是 Spring 管理的 bean）
    @Autowired
    private com.chixing.controller.RefundsController refundsController;

    @RabbitListener(queues = RabbitMQConfig.CANCEL_ORDER_QUEUE)
    @RabbitHandler
    @Transactional(rollbackFor = Exception.class)
    public void handleCancelEarly(Map<String,Object> msg,Message message, Channel channel){

        try {
            System.out.println("cancel队列收到的msg：" + msg);

            // 安全转换 Number -> long/double，避免 ClassCastException
            Long orderId = ((Number) msg.get("orderId")).longValue();
            Long cancelId = ((Number) msg.get("cancelId")).longValue();
            Double penaltyAmount = ((Number) msg.get("penaltyAmount")).doubleValue();
            Double backPrice = ((Number) msg.get("backPrice")).doubleValue();
            Double orderPrice = ((Number) msg.get("orderPrice")).doubleValue();
            String penaltyDesc = (String) msg.get("penaltyDesc");

            //根据订单 id 获得当前订单
            Orders order = ordersMapper.selectById(orderId);

            //检查订单是否存在
            if (order == null){
                System.out.println("订单不存在，忽略消息 orderId = " + orderId);
                channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
                return;
            }

            //幂等性检查，如果订单已经取消，仅做补偿，防止重复penalty
            if ("已取消".equals(order.getOrderStatus())){
                System.out.println("订单已经取消，忽略重复消息 orderId = " + orderId);
                channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
                return;
            }

            //检查是否已经有penalty（避免重复插入）
            Long penaltyCount = penaltyMapper.selectCount(
                    new QueryWrapper<Penalty>().eq("order_id",orderId)
            );
            if (penaltyCount != null && penaltyCount > 0){
                System.out.println("已经存在penalty记录,跳过 penalty 插入 orderId = " + orderId);
            }else {
                //插入penalty
                Penalty penalty = new Penalty();
                penalty.setOrderId(orderId);
                penalty.setRoomPriceAtBooking(orderPrice);
                penalty.setCancelId(cancelId);
                penalty.setPenaltyAmount(penaltyAmount);
                penalty.setPenaltyDesc(penaltyDesc);
                penalty.setCreatedAt(LocalDateTime.now());
                penalty.setRefundAmountToUser(backPrice);
                penaltyMapper.insert(penalty);
            }

            //更新订单状态
            order.setOrderStatus("已取消");
            ordersMapper.updateById(order);

            //检查是否已经有退款记录表（已完成或处理中）——如果有且成功则跳过退款调用
            Long refundExists = refundsMapper.selectCount(
                    new QueryWrapper<Refunds>().eq("order_id",orderId)
            );


            if (refundExists != null && refundExists > 0){
                System.out.println("订单已有退款记录，跳过调用退款接口。orderId=" + orderId);
            }else {
                //构建 RefundsDTO 并调用 已存在的 RefundsController.refund(...) 复用业务逻辑
                RefundsDTO dto = new RefundsDTO();
                dto.setOrderId(orderId);
                dto.setOrderNumber(order.getOrderNumber());
                dto.setRefundAmount(BigDecimal.valueOf(backPrice));
                dto.setRefundReason("订单提前取消，返还部分房费");

                System.out.println("[退款前] 调用支付宝沙箱退款接口 => orderNumber="
                        + order.getOrderNumber()
                        + " refundAmount=" + backPrice
                        + " orderId=" + orderId);

                Result refundResult = refundsController.refund(dto);

                //判定退款的结果
                if (refundResult == null || refundResult.getCode() != 200){
                    System.out.println("退款操作失败，refundResult=" + refundResult);
                    //channel.basicNack(message.getMessageProperties().getDeliveryTag(),false,true);
                    //return;
                    throw new RuntimeException("退款失败，触发事务回滚：" + refundResult);

                }

            }

            System.out.println("释放库存前日志...");
            //释放已经预定的库存
            roomInventoryService.releaseBookedInventory(orderId);
            System.out.println("释放库存后日志...");

            //消息确认
            // requeue: false 不重入队列，立马删除 ； true:重入队列,不删除
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
            System.out.println("已支付订单取消成功...");

        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("已支付订单取消失败...");
            try {
                // 出现异常，重入队列重试
                channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, false);
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }

    }


}



